Micron Document
πŸŽ–οΈGitΠ―Ρ€Π°πŸŽ–οΈ


Displaying Raw β€’ Download

feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/ota/Esp32OtaUpdateHandler.kt 2d20cd8a4708e2ef66e98d5259f3a97ec93f240a (2d20cd8a) Text, 30.78 KB

T8b949e/*
* Copyright (c) 2026 Meshtastic LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
Tff7b72package T7ee787org.meshtastic.feature.firmware.ota

Tff7b72import T7ee787co.touchlab.kermit.Logger
Tff7b72import T7ee787kotlinx.coroutines.CancellationException
Tff7b72import T7ee787kotlinx.coroutines.CoroutineDispatcher
Tff7b72import T7ee787kotlinx.coroutines.NonCancellable
Tff7b72import T7ee787kotlinx.coroutines.flow.first
Tff7b72import T7ee787kotlinx.coroutines.withContext
Tff7b72import T7ee787kotlinx.coroutines.withTimeoutOrNull
Tff7b72import T7ee787org.koin.core.annotation.Single
Tff7b72import T7ee787org.meshtastic.core.ble.BleConnectionFactory
Tff7b72import T7ee787org.meshtastic.core.ble.BleScanner
Tff7b72import T7ee787org.meshtastic.core.common.util.CommonUri
Tff7b72import T7ee787org.meshtastic.core.common.util.ioDispatcher
Tff7b72import T7ee787org.meshtastic.core.database.entity.FirmwareRelease
Tff7b72import T7ee787org.meshtastic.core.di.CoroutineDispatchers
Tff7b72import T7ee787org.meshtastic.core.model.DeviceHardware
Tff7b72import T7ee787org.meshtastic.core.model.util.isOtaStatusNotification
Tff7b72import T7ee787org.meshtastic.core.repository.FirmwareUpdateStatusRepository
Tff7b72import T7ee787org.meshtastic.core.repository.NodeRepository
Tff7b72import T7ee787org.meshtastic.core.repository.RadioController
Tff7b72import T7ee787org.meshtastic.core.resources.Res
Tff7b72import T7ee787org.meshtastic.core.resources.UiText
Tff7b72import T7ee787org.meshtastic.core.resources.firmware_update_connecting_attempt
Tff7b72import T7ee787org.meshtastic.core.resources.firmware_update_downloading_percent
Tff7b72import T7ee787org.meshtastic.core.resources.firmware_update_erasing
Tff7b72import T7ee787org.meshtastic.core.resources.firmware_update_extracting
Tff7b72import T7ee787org.meshtastic.core.resources.firmware_update_hash_rejected
Tff7b72import T7ee787org.meshtastic.core.resources.firmware_update_not_found_in_release
Tff7b72import T7ee787org.meshtastic.core.resources.firmware_update_ota_failed
Tff7b72import T7ee787org.meshtastic.core.resources.firmware_update_ota_unsupported_reason
Tff7b72import T7ee787org.meshtastic.core.resources.firmware_update_searching_device
Tff7b72import T7ee787org.meshtastic.core.resources.firmware_update_starting_ota
Tff7b72import T7ee787org.meshtastic.core.resources.firmware_update_uploading
Tff7b72import T7ee787org.meshtastic.core.resources.firmware_update_waiting_reboot
Tff7b72import T7ee787org.meshtastic.core.resources.getStringSuspend
Tff7b72import T7ee787org.meshtastic.feature.firmware.FirmwareArtifact
Tff7b72import T7ee787org.meshtastic.feature.firmware.FirmwareFileHandler
Tff7b72import T7ee787org.meshtastic.feature.firmware.FirmwareRetriever
Tff7b72import T7ee787org.meshtastic.feature.firmware.FirmwareUpdateHandler
Tff7b72import T7ee787org.meshtastic.feature.firmware.FirmwareUpdateState
Tff7b72import T7ee787org.meshtastic.feature.firmware.ProgressState
Tff7b72import T7ee787org.meshtastic.feature.firmware.stripFormatArgs

Tff7b72private Tff7b72const Tff7b72val Te6edf3RETRY_DELAY Tff7b72= T79c0ff2T79c0ff0T79c0ff0T79c0ff0L
Tff7b72private Tff7b72const Tff7b72val Te6edf3PERCENT_MAX Tff7b72= T79c0ff1T79c0ff0T79c0ff0
Tff7b72private Tff7b72const Tff7b72val Te6edf3REBOOT_MODE_BLE Tff7b72= T79c0ff1
Tff7b72private Tff7b72const Tff7b72val Te6edf3REBOOT_MODE_WIFI Tff7b72= T79c0ff2

T8b949e// Time to wait for the firmware to confirm OTA entry before tearing down the mesh transport.
T8b949e// The firmware emits a ClientNotification ("Rebooting to <mode> OTA" on success, a "Cannot start OTA: ..." /
T8b949e// "OTA Loader does not support ..." warning on rejection) before its 1 s scheduled reboot β€” so this only needs to
T8b949e// cover BLE/TCP round-trip + device processing, not the reboot itself.
Tff7b72private Tff7b72const Tff7b72val Te6edf3OTA_PREFLIGHT_TIMEOUT_MS Tff7b72= T79c0ff5T79c0ff0T79c0ff0T79c0ff0L

T8b949e// Time to wait for BLE GATT to fully release after disconnecting mesh service
Tff7b72private Tff7b72const Tff7b72val Te6edf3GATT_RELEASE_DELAY_MS Tff7b72= T79c0ff1T79c0ff0T79c0ff0T79c0ff0L

T8b949e// Wi-Fi OTA loader needs time to reboot, rejoin the network, and start its TCP OTA server after confirmation.
Tff7b72private Tff7b72const Tff7b72val Te6edf3WIFI_OTA_READINESS_DELAY_MS Tff7b72= T79c0ff8Te6edf3_000L

T8b949e// Firmware emits one of these human-readable messages via meshtastic.ClientNotification before rebooting.
T8b949e// "Rebooting to BLE OTA" / "Rebooting to WiFi OTA" -> success. Any other OTA status message -> rejection.
Tff7b72private Tff7b72const Tff7b72val Te6edf3OTA_CONFIRM_PREFIX Tff7b72= Ta5d6ff"Ta5d6ffRebooting toTa5d6ff"

T8b949e// A BLE target is a 6-octet MAC (e.g. AA:BB:CC:DD:EE:FF). Matching the exact MAC shape β€” not merely "contains a colon"
T8b949e// β€” keeps an IPv6 WiFi target (which also has colons) from being misrouted to the BLE path.
Tff7b72private Tff7b72val Te6edf3MAC_ADDRESS_REGEX Tff7b72= Te6edf3RegexTb4b4b4(Ta5d6ff"Ta5d6ff([0-9A-Fa-f]{2}:){5}[0-9A-Fa-f]{2}Ta5d6ff"Tb4b4b4)

Tff7b72private Tff7b72data Tff7b72class T56d364TransportFactoryResolutionTb4b4b4(
Tff7b72val Te6edf3factoryTb4b4b4: Tb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Te6edf3UnifiedOtaProtocolTb4b4b4,
Tff7b72val Te6edf3readinessAlreadyWaitedTb4b4b4: Tffa657BooleanTb4b4b4,
Tb4b4b4)

Tff7b72interface T56d364Esp32OtaUpdateEnvironment Tb4b4b4{
Tff7b72val Te6edf3otaPreflightTimeoutMsTb4b4b4: Tffa657Long
Tff7b72val Te6edf3otaTransportRetryDelayMsTb4b4b4: Tffa657Long
Tff7b72val Te6edf3wifiOtaReadinessDelayMsTb4b4b4: Tffa657Long
Tff7b72val Te6edf3gattReleaseDelayMsTb4b4b4: Tffa657Long
Tff7b72val Te6edf3wifiDiscoveryEnabledTb4b4b4: Tffa657Boolean

Tff7b72suspend Tff7b72fun Td2a8ffdelayTb4b4b4(Te6edf3millisecondsTb4b4b4: Tffa657LongTb4b4b4)

Tff7b72fun Td2a8ffcreateBleTransportTb4b4b4(
Te6edf3bleScannerTb4b4b4: Te6edf3BleScannerTb4b4b4,
Te6edf3bleConnectionFactoryTb4b4b4: Te6edf3BleConnectionFactoryTb4b4b4,
Te6edf3addressTb4b4b4: Tffa657StringTb4b4b4,
Te6edf3dispatcherTb4b4b4: Te6edf3CoroutineDispatcherTb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3UnifiedOtaProtocol

Tff7b72fun Td2a8ffcreateWifiTransportTb4b4b4(Te6edf3deviceIpTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Te6edf3UnifiedOtaProtocol

Tff7b72suspend Tff7b72fun Td2a8ffdiscoverWifiOtaDeviceTb4b4b4(Tb4b4b4)Tb4b4b4: Tffa657String?
Tb4b4b4}

Tf0883e@SingleTb4b4b4(Te6edf3binds Tff7b72= Tff7b72[Te6edf3Esp32OtaUpdateEnvironmentTff7b72::Te6edf3classTff7b72]Tb4b4b4)
Tff7b72class T56d364DefaultEsp32OtaUpdateEnvironment Tb4b4b4: Te6edf3Esp32OtaUpdateEnvironment Tb4b4b4{
Tff7b72override Tff7b72val Te6edf3otaPreflightTimeoutMsTb4b4b4: Tffa657Long Tff7b72= Te6edf3OTA_PREFLIGHT_TIMEOUT_MS
Tff7b72override Tff7b72val Te6edf3otaTransportRetryDelayMsTb4b4b4: Tffa657Long Tff7b72= Te6edf3RETRY_DELAY
Tff7b72override Tff7b72val Te6edf3wifiOtaReadinessDelayMsTb4b4b4: Tffa657Long Tff7b72= Te6edf3WIFI_OTA_READINESS_DELAY_MS
Tff7b72override Tff7b72val Te6edf3gattReleaseDelayMsTb4b4b4: Tffa657Long Tff7b72= Te6edf3GATT_RELEASE_DELAY_MS
Tff7b72override Tff7b72val Te6edf3wifiDiscoveryEnabledTb4b4b4: Tffa657Boolean Tff7b72= Tff7b72true

Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffdelayTb4b4b4(Te6edf3millisecondsTb4b4b4: Tffa657LongTb4b4b4) Tb4b4b4{
Te6edf3kotlinxTb4b4b4.Te6edf3coroutinesTb4b4b4.Te6edf3delayTb4b4b4(Te6edf3millisecondsTb4b4b4)
Tb4b4b4}

Tff7b72override Tff7b72fun Td2a8ffcreateBleTransportTb4b4b4(
Te6edf3bleScannerTb4b4b4: Te6edf3BleScannerTb4b4b4,
Te6edf3bleConnectionFactoryTb4b4b4: Te6edf3BleConnectionFactoryTb4b4b4,
Te6edf3addressTb4b4b4: Tffa657StringTb4b4b4,
Te6edf3dispatcherTb4b4b4: Te6edf3CoroutineDispatcherTb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3UnifiedOtaProtocol Tff7b72= Te6edf3BleOtaTransportTb4b4b4(Te6edf3bleScannerTb4b4b4, Te6edf3bleConnectionFactoryTb4b4b4, Te6edf3addressTb4b4b4, Te6edf3dispatcherTb4b4b4)

Tff7b72override Tff7b72fun Td2a8ffcreateWifiTransportTb4b4b4(Te6edf3deviceIpTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Te6edf3UnifiedOtaProtocol Tff7b72= Te6edf3WifiOtaTransportTb4b4b4(Te6edf3deviceIpTb4b4b4)

Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffdiscoverWifiOtaDeviceTb4b4b4(Tb4b4b4)Tb4b4b4: Tffa657String? Tff7b72= Te6edf3WifiOtaDiscoveryTb4b4b4.Te6edf3discoverOtaDeviceTb4b4b4(Tb4b4b4)
Tb4b4b4}

Tff7b72internal Tff7b72fun Td2a8ffisBleMacAddressTb4b4b4(Te6edf3targetTb4b4b4: Tffa657StringTb4b4b4)Tb4b4b4: Tffa657Boolean Tff7b72= Te6edf3MAC_ADDRESS_REGEXTb4b4b4.Te6edf3matchesTb4b4b4(Te6edf3targetTb4b4b4)

T8b949e/**
* KMP handler for ESP32 firmware updates using the Unified OTA protocol. Supports both BLE and WiFi/TCP transports via
* [UnifiedOtaProtocol].
*
* All platform I/O (file reading, content-resolver imports) is delegated to [FirmwareFileHandler].
*/
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffTooManyFunctionsTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffLongParameterListTa5d6ff"Tb4b4b4)
Tf0883e@Single
Tff7b72class T56d364Esp32OtaUpdateHandlerTb4b4b4(
Tff7b72private Tff7b72val Te6edf3firmwareRetrieverTb4b4b4: Te6edf3FirmwareRetrieverTb4b4b4,
Tff7b72private Tff7b72val Te6edf3firmwareFileHandlerTb4b4b4: Te6edf3FirmwareFileHandlerTb4b4b4,
Tff7b72private Tff7b72val Te6edf3radioControllerTb4b4b4: Te6edf3RadioControllerTb4b4b4,
Tff7b72private Tff7b72val Te6edf3nodeRepositoryTb4b4b4: Te6edf3NodeRepositoryTb4b4b4,
Tff7b72private Tff7b72val Te6edf3firmwareUpdateStatusRepositoryTb4b4b4: Te6edf3FirmwareUpdateStatusRepositoryTb4b4b4,
Tff7b72private Tff7b72val Te6edf3environmentTb4b4b4: Te6edf3Esp32OtaUpdateEnvironmentTb4b4b4,
Tff7b72private Tff7b72val Te6edf3bleScannerTb4b4b4: Te6edf3BleScannerTb4b4b4,
Tff7b72private Tff7b72val Te6edf3bleConnectionFactoryTb4b4b4: Te6edf3BleConnectionFactoryTb4b4b4,
Tff7b72private Tff7b72val Te6edf3dispatchersTb4b4b4: Te6edf3CoroutineDispatchersTb4b4b4,
Tb4b4b4) Tb4b4b4: Te6edf3FirmwareUpdateHandler Tb4b4b4{

T8b949e/** Entry point for FirmwareUpdateHandler interface. Routes to BLE (target is a MAC) or WiFi (anything else). */
Tff7b72override Tff7b72suspend Tff7b72fun Td2a8ffstartUpdateTb4b4b4(
Te6edf3releaseTb4b4b4: Te6edf3FirmwareReleaseTb4b4b4,
Te6edf3hardwareTb4b4b4: Te6edf3DeviceHardwareTb4b4b4,
Te6edf3targetTb4b4b4: Tffa657StringTb4b4b4,
Te6edf3updateStateTb4b4b4: Tb4b4b4(Te6edf3FirmwareUpdateStateTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Te6edf3firmwareUriTb4b4b4: Te6edf3CommonUri?Tb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3FirmwareArtifact? Tff7b72= Tff7b72if Tb4b4b4(Te6edf3isBleMacAddressTb4b4b4(Te6edf3targetTb4b4b4)Tb4b4b4) Tb4b4b4{
Te6edf3startBleUpdateTb4b4b4(Te6edf3releaseTb4b4b4, Te6edf3hardwareTb4b4b4, Te6edf3targetTb4b4b4, Te6edf3updateStateTb4b4b4, Te6edf3firmwareUriTb4b4b4)
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3startWifiUpdateTb4b4b4(Te6edf3releaseTb4b4b4, Te6edf3hardwareTb4b4b4, Te6edf3targetTb4b4b4, Te6edf3updateStateTb4b4b4, Te6edf3firmwareUriTb4b4b4)
Tb4b4b4}

Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffstartBleUpdateTb4b4b4(
Te6edf3releaseTb4b4b4: Te6edf3FirmwareReleaseTb4b4b4,
Te6edf3hardwareTb4b4b4: Te6edf3DeviceHardwareTb4b4b4,
Te6edf3addressTb4b4b4: Tffa657StringTb4b4b4,
Te6edf3updateStateTb4b4b4: Tb4b4b4(Te6edf3FirmwareUpdateStateTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Te6edf3firmwareUriTb4b4b4: Te6edf3CommonUri? Tff7b72= Tff7b72nullTb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3FirmwareArtifact? Tff7b72= Te6edf3performUpdateTb4b4b4(
Te6edf3release Tff7b72= Te6edf3releaseTb4b4b4,
Te6edf3hardware Tff7b72= Te6edf3hardwareTb4b4b4,
Te6edf3updateState Tff7b72= Te6edf3updateStateTb4b4b4,
Te6edf3firmwareUri Tff7b72= Te6edf3firmwareUriTb4b4b4,
Te6edf3transportFactory Tff7b72= Tb4b4b4{
Te6edf3environmentTb4b4b4.Te6edf3createBleTransportTb4b4b4(Te6edf3bleScannerTb4b4b4, Te6edf3bleConnectionFactoryTb4b4b4, Te6edf3addressTb4b4b4, Te6edf3dispatchersTb4b4b4.Te6edf3defaultTb4b4b4)
Tb4b4b4}Tb4b4b4,
Te6edf3rebootMode Tff7b72= Te6edf3REBOOT_MODE_BLETb4b4b4,
Te6edf3connectionAttempts Tff7b72= T79c0ff5Tb4b4b4,
Te6edf3postConfirmReadinessDelayMs Tff7b72= T79c0ff0LTb4b4b4,
Tb4b4b4)

Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffstartWifiUpdateTb4b4b4(
Te6edf3releaseTb4b4b4: Te6edf3FirmwareReleaseTb4b4b4,
Te6edf3hardwareTb4b4b4: Te6edf3DeviceHardwareTb4b4b4,
Te6edf3deviceIpTb4b4b4: Tffa657StringTb4b4b4,
Te6edf3updateStateTb4b4b4: Tb4b4b4(Te6edf3FirmwareUpdateStateTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Te6edf3firmwareUriTb4b4b4: Te6edf3CommonUri? Tff7b72= Tff7b72nullTb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3FirmwareArtifact? Tff7b72= Te6edf3performUpdateTb4b4b4(
Te6edf3release Tff7b72= Te6edf3releaseTb4b4b4,
Te6edf3hardware Tff7b72= Te6edf3hardwareTb4b4b4,
Te6edf3updateState Tff7b72= Te6edf3updateStateTb4b4b4,
Te6edf3firmwareUri Tff7b72= Te6edf3firmwareUriTb4b4b4,
Te6edf3transportFactory Tff7b72= Tb4b4b4{ Te6edf3environmentTb4b4b4.Te6edf3createWifiTransportTb4b4b4(Te6edf3deviceIpTb4b4b4) Tb4b4b4}Tb4b4b4,
Te6edf3rebootMode Tff7b72= Te6edf3REBOOT_MODE_WIFITb4b4b4,
Te6edf3connectionAttempts Tff7b72= T79c0ff1T79c0ff0Tb4b4b4,
Te6edf3postConfirmReadinessDelayMs Tff7b72= Te6edf3environmentTb4b4b4.Te6edf3wifiOtaReadinessDelayMsTb4b4b4,
Tb4b4b4)

Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffperformUpdateTb4b4b4(
Te6edf3releaseTb4b4b4: Te6edf3FirmwareReleaseTb4b4b4,
Te6edf3hardwareTb4b4b4: Te6edf3DeviceHardwareTb4b4b4,
Te6edf3updateStateTb4b4b4: Tb4b4b4(Te6edf3FirmwareUpdateStateTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Te6edf3firmwareUriTb4b4b4: Te6edf3CommonUri?Tb4b4b4,
Te6edf3transportFactoryTb4b4b4: Tb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Te6edf3UnifiedOtaProtocolTb4b4b4,
Te6edf3rebootModeTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3connectionAttemptsTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3postConfirmReadinessDelayMsTb4b4b4: Tffa657LongTb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3FirmwareArtifact? Tb4b4b4{
Te6edf3firmwareUpdateStatusRepositoryTb4b4b4.Te6edf3beginOtaUpdateTb4b4b4(Tb4b4b4)
Tff7b72var Te6edf3cleanupArtifactTb4b4b4: Te6edf3FirmwareArtifact? Tff7b72= Tff7b72null
Tff7b72return Tff7b72try Tb4b4b4{
Tff7b72try Tb4b4b4{
Te6edf3withContextTb4b4b4(Te6edf3ioDispatcherTb4b4b4) Tb4b4b4{
Te6edf3performUpdateFlowTb4b4b4(
Te6edf3releaseTb4b4b4,
Te6edf3hardwareTb4b4b4,
Te6edf3firmwareUriTb4b4b4,
Te6edf3updateStateTb4b4b4,
Te6edf3transportFactoryTb4b4b4,
Te6edf3rebootModeTb4b4b4,
Te6edf3connectionAttemptsTb4b4b4,
Te6edf3postConfirmReadinessDelayMsTb4b4b4,
Tb4b4b4)
Tb4b4b4.Te6edf3also Tb4b4b4{ Te6edf3cleanupArtifact Tff7b72= Tffa657it Tb4b4b4}
Tb4b4b4}
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3eTb4b4b4: Te6edf3CancellationExceptionTb4b4b4) Tb4b4b4{
Tff7b72throw Te6edf3e
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3eTb4b4b4: Te6edf3OtaProtocolExceptionTb4b4b4.Te6edf3HashRejectedTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3eTb4b4b4(Te6edf3eTb4b4b4) Tb4b4b4{ Ta5d6ff"Ta5d6ffESP32 OTA: Hash rejected by deviceTa5d6ff" Tb4b4b4}
Te6edf3updateStateTb4b4b4(Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3ErrorTb4b4b4(Te6edf3UiTextTb4b4b4.Te6edf3ResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_update_hash_rejectedTb4b4b4)Tb4b4b4)Tb4b4b4)
Te6edf3cleanupArtifact
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3eTb4b4b4: Te6edf3OtaProtocolExceptionTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3eTb4b4b4(Te6edf3eTb4b4b4) Tb4b4b4{ Ta5d6ff"Ta5d6ffESP32 OTA: Protocol errorTa5d6ff" Tb4b4b4}
Te6edf3updateStateTb4b4b4(
Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3ErrorTb4b4b4(Te6edf3UiTextTb4b4b4.Te6edf3ResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_update_ota_failedTb4b4b4, Te6edf3eTb4b4b4.Te6edf3message Tff7b72?: Ta5d6ff"Ta5d6ff"Tb4b4b4)Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3cleanupArtifact
Tb4b4b4} Tff7b72catch Tb4b4b4(Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffTooGenericExceptionCaughtTa5d6ff"Tb4b4b4) Te6edf3eTb4b4b4: Te6edf3ExceptionTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3eTb4b4b4(Te6edf3eTb4b4b4) Tb4b4b4{ Ta5d6ff"Ta5d6ffESP32 OTA: Unexpected errorTa5d6ff" Tb4b4b4}
Te6edf3updateStateTb4b4b4(
Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3ErrorTb4b4b4(Te6edf3UiTextTb4b4b4.Te6edf3ResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_update_ota_failedTb4b4b4, Te6edf3eTb4b4b4.Te6edf3message Tff7b72?: Ta5d6ff"Ta5d6ff"Tb4b4b4)Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3cleanupArtifact
Tb4b4b4}
Tb4b4b4} Tff7b72finally Tb4b4b4{
Te6edf3firmwareUpdateStatusRepositoryTb4b4b4.Te6edf3endOtaUpdateTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

T8b949e// Extracted from performUpdate to stay under detekt LongMethod threshold (60). Single linear OTA flow:
T8b949e// obtain file β†’ hash β†’ preflight (clearClientNotification + trigger + runOtaPreflight) β†’ connect β†’ execute.
Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffReturnCountTa5d6ff"Tb4b4b4) T8b949e// three distinct early-exit paths in a linear pipeline
Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffperformUpdateFlowTb4b4b4(
Te6edf3releaseTb4b4b4: Te6edf3FirmwareReleaseTb4b4b4,
Te6edf3hardwareTb4b4b4: Te6edf3DeviceHardwareTb4b4b4,
Te6edf3firmwareUriTb4b4b4: Te6edf3CommonUri?Tb4b4b4,
Te6edf3updateStateTb4b4b4: Tb4b4b4(Te6edf3FirmwareUpdateStateTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Te6edf3transportFactoryTb4b4b4: Tb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Te6edf3UnifiedOtaProtocolTb4b4b4,
Te6edf3rebootModeTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3connectionAttemptsTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3postConfirmReadinessDelayMsTb4b4b4: Tffa657LongTb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3FirmwareArtifact? Tb4b4b4{
T8b949e// Step 1: Get firmware file
Tff7b72val Te6edf3firmwareFile Tff7b72= Te6edf3obtainFirmwareFileTb4b4b4(Te6edf3releaseTb4b4b4, Te6edf3hardwareTb4b4b4, Te6edf3firmwareUriTb4b4b4, Te6edf3updateStateTb4b4b4) Tff7b72?: Tff7b72return Tff7b72null

T8b949e// Step 2: Read firmware once and calculate hash
Tff7b72val Te6edf3firmwareBytes Tff7b72= Te6edf3firmwareFileHandlerTb4b4b4.Te6edf3readBytesTb4b4b4(Te6edf3firmwareFileTb4b4b4)
Tff7b72val Te6edf3sha256Bytes Tff7b72= Te6edf3FirmwareHashUtilTb4b4b4.Te6edf3calculateSha256BytesTb4b4b4(Te6edf3firmwareBytesTb4b4b4)
Tff7b72val Te6edf3sha256Hash Tff7b72= Te6edf3FirmwareHashUtilTb4b4b4.Te6edf3bytesToHexTb4b4b4(Te6edf3sha256BytesTb4b4b4)
Te6edf3LoggerTb4b4b4.Te6edf3i Tb4b4b4{ Ta5d6ff"Ta5d6ffESP32 OTA: Firmware hash: Tffd700$Te6edf3sha256HashTa5d6ff (Tffd700${Te6edf3firmwareBytesTb4b4b4.Te6edf3sizeTffd700}Ta5d6ff bytes)Ta5d6ff" Tb4b4b4}

Te6edf3firmwareUpdateStatusRepositoryTb4b4b4.Te6edf3beginOtaPreflightTb4b4b4(Tb4b4b4)
Tff7b72try Tb4b4b4{
T8b949e// Clear any stale notification from a previous attempt so the firmware's response is always
T8b949e// treated as a fresh emission (StateFlow conflates identical values β€” a retry would otherwise
T8b949e// time out waiting for a value that the flow already holds).
Te6edf3radioControllerTb4b4b4.Te6edf3clearClientNotificationTb4b4b4(Tb4b4b4)

T8b949e// Snapshot the current notification message BEFORE sending the OTA trigger. After the clear
T8b949e// above this is null today, but capturing it remains defense-in-depth: if the clear is ever
T8b949e// removed or the value is repopulated between the clear and the trigger, the predicate must
T8b949e// still distinguish the response from a pre-existing value.
Tff7b72val Te6edf3baselineMessage Tff7b72= Te6edf3radioControllerTb4b4b4.Te6edf3clientNotificationTb4b4b4.Te6edf3valueTff7b72?.Te6edf3message
Te6edf3triggerRebootOtaTb4b4b4(Te6edf3rebootModeTb4b4b4, Te6edf3sha256BytesTb4b4b4)

T8b949e// Step 3: Preflight β€” wait for the firmware to confirm OTA entry BEFORE tearing down the mesh
T8b949e// transport. The mesh ACK only confirms packet delivery; it does NOT mean the device's OTA
T8b949e// loader supports the requested transport. The firmware surfaces loader-capability failures as
T8b949e// a meshtastic.ClientNotification warning ("OTA Loader does not support <mode>", etc.) emitted
T8b949e// before its scheduled reboot, so we race that signal against a bounded timeout. On rejection
T8b949e// we fail fast and leave the mesh connection intact. On timeout we preserve legacy behavior by
T8b949e// releasing the mesh transport and trying the OTA path; older firmware may be silent.
Tff7b72if Tb4b4b4(Tff7b72!Te6edf3runOtaPreflightTb4b4b4(Te6edf3rebootModeTb4b4b4, Te6edf3baselineMessageTb4b4b4, Te6edf3updateStateTb4b4b4)Tb4b4b4.Te6edf3continueToOtaTransportTb4b4b4) Tb4b4b4{
Tff7b72return Te6edf3firmwareFile
Tb4b4b4}
Tb4b4b4} Tff7b72finally Tb4b4b4{
Te6edf3firmwareUpdateStatusRepositoryTb4b4b4.Te6edf3finishOtaPreflightTb4b4b4(Tb4b4b4)
Tb4b4b4}

Tff7b72val Te6edf3transport Tff7b72=
Te6edf3connectToDeviceTb4b4b4(
Te6edf3transportFactory Tff7b72= Te6edf3transportFactoryTb4b4b4,
Te6edf3attempts Tff7b72= Te6edf3connectionAttemptsTb4b4b4,
Te6edf3rebootMode Tff7b72= Te6edf3rebootModeTb4b4b4,
Te6edf3postConfirmReadinessDelayMs Tff7b72= Te6edf3postConfirmReadinessDelayMsTb4b4b4,
Te6edf3updateState Tff7b72= Te6edf3updateStateTb4b4b4,
Tb4b4b4)

Tff7b72try Tb4b4b4{
Te6edf3executeOtaSequenceTb4b4b4(Te6edf3transportTb4b4b4, Te6edf3firmwareBytesTb4b4b4, Te6edf3sha256HashTb4b4b4, Te6edf3rebootModeTb4b4b4, Te6edf3updateStateTb4b4b4)
Tff7b72return Te6edf3firmwareFile
Tb4b4b4} Tff7b72finally Tb4b4b4{
Te6edf3transportTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

Tff7b72private Tff7b72suspend Tff7b72fun Td2a8fftriggerRebootOtaTb4b4b4(Te6edf3modeTb4b4b4: Tffa657IntTb4b4b4, Te6edf3hashTb4b4b4: Te6edf3ByteArray?Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3myInfo Tff7b72= Te6edf3nodeRepositoryTb4b4b4.Te6edf3myNodeInfoTb4b4b4.Te6edf3value Tff7b72?: Tff7b72return
Tff7b72val Te6edf3myNodeNum Tff7b72= Te6edf3myInfoTb4b4b4.Te6edf3myNodeNum
Te6edf3LoggerTb4b4b4.Te6edf3i Tb4b4b4{ Ta5d6ff"Ta5d6ffESP32 OTA: Triggering reboot OTA mode Tffd700$Te6edf3modeTa5d6ff with hashTa5d6ff" Tb4b4b4}
Te6edf3radioControllerTb4b4b4.Te6edf3requestRebootOtaTb4b4b4(Te6edf3radioControllerTb4b4b4.Te6edf3generatePacketIdTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3myNodeNumTb4b4b4, Te6edf3modeTb4b4b4, Te6edf3hashTb4b4b4)
Tb4b4b4}

T8b949e/**
* Disconnect the mesh service BLE connection to free up the GATT for OTA. Setting device address to "n" (NOP
* interface) cleanly disconnects without reconnection attempts.
*/
Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffdisconnectMeshServiceTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3i Tb4b4b4{ Ta5d6ff"Ta5d6ffESP32 OTA: Disconnecting mesh service for OTATa5d6ff" Tb4b4b4}
Te6edf3radioControllerTb4b4b4.Te6edf3setDeviceAddressTb4b4b4(Ta5d6ff"Ta5d6ffnTa5d6ff"Tb4b4b4)
Tb4b4b4}

Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffobtainFirmwareFileTb4b4b4(
Te6edf3releaseTb4b4b4: Te6edf3FirmwareReleaseTb4b4b4,
Te6edf3hardwareTb4b4b4: Te6edf3DeviceHardwareTb4b4b4,
Te6edf3firmwareUriTb4b4b4: Te6edf3CommonUri?Tb4b4b4,
Te6edf3updateStateTb4b4b4: Tb4b4b4(Te6edf3FirmwareUpdateStateTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3FirmwareArtifact? Tff7b72= Tff7b72if Tb4b4b4(Te6edf3firmwareUri Tff7b72!Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Te6edf3updateStateTb4b4b4(
Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3ProcessingTb4b4b4(
Te6edf3ProgressStateTb4b4b4(Te6edf3message Tff7b72= Te6edf3UiTextTb4b4b4.Te6edf3ResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_update_extractingTb4b4b4)Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3firmwareFileHandlerTb4b4b4.Te6edf3importFromUriTb4b4b4(Te6edf3firmwareUriTb4b4b4)
Tb4b4b4} Tff7b72else Tb4b4b4{
Tff7b72val Te6edf3downloadingMsg Tff7b72= Te6edf3getStringSuspendTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_update_downloading_percentTb4b4b4, T79c0ff0Tb4b4b4)Tb4b4b4.Te6edf3stripFormatArgsTb4b4b4(Tb4b4b4)

Te6edf3updateStateTb4b4b4(
Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3DownloadingTb4b4b4(
Te6edf3ProgressStateTb4b4b4(Te6edf3message Tff7b72= Te6edf3UiTextTb4b4b4.Te6edf3DynamicStringTb4b4b4(Te6edf3downloadingMsgTb4b4b4)Tb4b4b4, Te6edf3progress Tff7b72= T79c0ff0fTb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)

Tff7b72val Te6edf3firmwareFile Tff7b72=
Te6edf3firmwareRetrieverTb4b4b4.Te6edf3retrieveEsp32FirmwareTb4b4b4(Te6edf3releaseTb4b4b4, Te6edf3hardwareTb4b4b4) Tb4b4b4{ Te6edf3progress Tff7b72-Tff7b72>
Tff7b72val Te6edf3percent Tff7b72= Tb4b4b4(Te6edf3progress Tff7b72* Te6edf3PERCENT_MAXTb4b4b4)Tb4b4b4.Te6edf3toIntTb4b4b4(Tb4b4b4)
Te6edf3updateStateTb4b4b4(
Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3DownloadingTb4b4b4(
Te6edf3ProgressStateTb4b4b4(
Te6edf3message Tff7b72= Te6edf3UiTextTb4b4b4.Te6edf3DynamicStringTb4b4b4(Te6edf3downloadingMsgTb4b4b4)Tb4b4b4,
Te6edf3progress Tff7b72= Te6edf3progressTb4b4b4,
Te6edf3details Tff7b72= Ta5d6ff"Tffd700$Te6edf3percentTa5d6ff%Ta5d6ff"Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tb4b4b4}

Tff7b72if Tb4b4b4(Te6edf3firmwareFile Tff7b72=Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Te6edf3updateStateTb4b4b4(
Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3ErrorTb4b4b4(
Te6edf3UiTextTb4b4b4.Te6edf3ResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_update_not_found_in_releaseTb4b4b4, Te6edf3hardwareTb4b4b4.Te6edf3displayNameTb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72null
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3firmwareFile
Tb4b4b4}
Tb4b4b4}

Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffconnectToDeviceTb4b4b4(
Te6edf3transportFactoryTb4b4b4: Tb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Te6edf3UnifiedOtaProtocolTb4b4b4,
Te6edf3attemptsTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3rebootModeTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3postConfirmReadinessDelayMsTb4b4b4: Tffa657LongTb4b4b4,
Te6edf3updateStateTb4b4b4: Tb4b4b4(Te6edf3FirmwareUpdateStateTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3UnifiedOtaProtocol Tb4b4b4{
Te6edf3updateStateTb4b4b4(
Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3ProcessingTb4b4b4(Te6edf3ProgressStateTb4b4b4(Te6edf3UiTextTb4b4b4.Te6edf3ResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_update_waiting_rebootTb4b4b4)Tb4b4b4)Tb4b4b4)Tb4b4b4,
Tb4b4b4)

Tff7b72val Te6edf3resolution Tff7b72= Te6edf3resolvePostConfirmTransportFactoryTb4b4b4(Te6edf3rebootModeTb4b4b4, Te6edf3transportFactoryTb4b4b4, Te6edf3updateStateTb4b4b4)
Te6edf3waitForPostConfirmReadinessTb4b4b4(Te6edf3rebootModeTb4b4b4, Te6edf3postConfirmReadinessDelayMsTb4b4b4, Te6edf3resolutionTb4b4b4.Te6edf3readinessAlreadyWaitedTb4b4b4)

Tff7b72return Te6edf3runTransportConnectRetriesTb4b4b4(Te6edf3resolutionTb4b4b4.Te6edf3factoryTb4b4b4, Te6edf3attemptsTb4b4b4, Te6edf3rebootModeTb4b4b4, Te6edf3updateStateTb4b4b4)
Tb4b4b4}

Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffresolvePostConfirmTransportFactoryTb4b4b4(
Te6edf3rebootModeTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3transportFactoryTb4b4b4: Tb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Te6edf3UnifiedOtaProtocolTb4b4b4,
Te6edf3updateStateTb4b4b4: Tb4b4b4(Te6edf3FirmwareUpdateStateTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3TransportFactoryResolution Tb4b4b4{
T8b949e// In WiFi mode the device may have picked up a different DHCP lease after rebooting into the OTA loader. Listen
T8b949e// for the loader's UDP discovery broadcast and, if one arrives, redirect the TCP transport at the discovered
T8b949e// IP.
Tff7b72if Tb4b4b4(Te6edf3rebootMode Tff7b72!Tff7b72= Te6edf3REBOOT_MODE_WIFI Tff7b72|Tff7b72| Tff7b72!Te6edf3environmentTb4b4b4.Te6edf3wifiDiscoveryEnabledTb4b4b4) Tb4b4b4{
Tff7b72return Te6edf3TransportFactoryResolutionTb4b4b4(Te6edf3factory Tff7b72= Te6edf3transportFactoryTb4b4b4, Te6edf3readinessAlreadyWaited Tff7b72= Tff7b72falseTb4b4b4)
Tb4b4b4}

Te6edf3updateStateTb4b4b4(
Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3ProcessingTb4b4b4(Te6edf3ProgressStateTb4b4b4(Te6edf3UiTextTb4b4b4.Te6edf3ResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_update_searching_deviceTb4b4b4)Tb4b4b4)Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72val Te6edf3discoveredIp Tff7b72= Te6edf3environmentTb4b4b4.Te6edf3discoverWifiOtaDeviceTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3factoryTb4b4b4: Tb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Te6edf3UnifiedOtaProtocol Tff7b72=
Tff7b72if Tb4b4b4(Te6edf3discoveredIp Tff7b72!Tff7b72= Tff7b72nullTb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3discoveredFactoryTb4b4b4: Tb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Te6edf3UnifiedOtaProtocol Tff7b72= Tb4b4b4{ Te6edf3environmentTb4b4b4.Te6edf3createWifiTransportTb4b4b4(Te6edf3discoveredIpTb4b4b4) Tb4b4b4}
Te6edf3LoggerTb4b4b4.Te6edf3i Tb4b4b4{ Ta5d6ff"Ta5d6ffESP32 OTA: Using UDP-discovered OTA device for TCP transportTa5d6ff" Tb4b4b4}
Te6edf3discoveredFactory
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3i Tb4b4b4{ Ta5d6ff"Ta5d6ffESP32 OTA: No UDP discovery broadcast received; falling back to configured device IPTa5d6ff" Tb4b4b4}
Te6edf3transportFactory
Tb4b4b4}
T8b949e// Only treat the readiness window as already-spent when discovery actually resolved a device β€” its bounded
T8b949e// listening window covers the loader's reboot+DHCP+TCP-server bring-up. On a null result (timeout / bind
T8b949e// failure) the device has not yet been heard from, so the caller's 8 s readiness margin still applies.
Tff7b72return Te6edf3TransportFactoryResolutionTb4b4b4(Te6edf3factory Tff7b72= Te6edf3factoryTb4b4b4, Te6edf3readinessAlreadyWaited Tff7b72= Te6edf3discoveredIp Tff7b72!Tff7b72= Tff7b72nullTb4b4b4)
Tb4b4b4}

Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffwaitForPostConfirmReadinessTb4b4b4(Te6edf3rebootModeTb4b4b4: Tffa657IntTb4b4b4, Te6edf3delayMsTb4b4b4: Tffa657LongTb4b4b4, Te6edf3readinessAlreadyWaitedTb4b4b4: Tffa657BooleanTb4b4b4) Tb4b4b4{
Tff7b72if Tb4b4b4(Te6edf3delayMs Tff7b72<Tff7b72= T79c0ff0L Tff7b72|Tff7b72| Te6edf3readinessAlreadyWaitedTb4b4b4) Tff7b72return
Te6edf3LoggerTb4b4b4.Te6edf3i Tb4b4b4{ Ta5d6ff"Ta5d6ffESP32 OTA: Waiting Tffd700${Te6edf3delayMsTffd700}Ta5d6ffms for Tffd700${Te6edf3otaModeNameTb4b4b4(Te6edf3rebootModeTb4b4b4)Tffd700}Ta5d6ff OTA serviceTa5d6ff" Tb4b4b4}
Te6edf3environmentTb4b4b4.Te6edf3delayTb4b4b4(Te6edf3delayMsTb4b4b4)
Tb4b4b4}

Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffrunTransportConnectRetriesTb4b4b4(
Te6edf3transportFactoryTb4b4b4: Tb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Te6edf3UnifiedOtaProtocolTb4b4b4,
Te6edf3attemptsTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3rebootModeTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3updateStateTb4b4b4: Tb4b4b4(Te6edf3FirmwareUpdateStateTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3UnifiedOtaProtocol Tb4b4b4{
Tff7b72var Te6edf3connectedTransportTb4b4b4: Te6edf3UnifiedOtaProtocol? Tff7b72= Tff7b72null
Tff7b72val Te6edf3result Tff7b72=
Te6edf3retryWithDelayTb4b4b4(
Te6edf3attempts Tff7b72= Te6edf3attemptsTb4b4b4,
Te6edf3retryDelayMillis Tff7b72= Te6edf3environmentTb4b4b4.Te6edf3otaTransportRetryDelayMsTb4b4b4,
Te6edf3onAttempt Tff7b72= Tb4b4b4{ Te6edf3i Tff7b72-Tff7b72>
Te6edf3updateStateTb4b4b4(
Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3ProcessingTb4b4b4(
Te6edf3ProgressStateTb4b4b4(Te6edf3UiTextTb4b4b4.Te6edf3ResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_update_connecting_attemptTb4b4b4, Te6edf3iTb4b4b4, Te6edf3attemptsTb4b4b4)Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tb4b4b4}Tb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3transport Tff7b72= Te6edf3transportFactoryTb4b4b4(Tb4b4b4)
Tff7b72val Te6edf3connectResult Tff7b72= Te6edf3connectTransportAttemptTb4b4b4(Te6edf3transportTb4b4b4, Te6edf3rebootModeTb4b4b4)
Te6edf3connectResultTb4b4b4.Te6edf3onSuccess Tb4b4b4{ Te6edf3connectedTransport Tff7b72= Te6edf3transport Tb4b4b4}
Tb4b4b4}

Te6edf3resultTb4b4b4.Te6edf3getOrElse Tb4b4b4{ Te6edf3cause Tff7b72-Tff7b72> Tff7b72throw Te6edf3postConfirmConnectionFailedTb4b4b4(Te6edf3rebootModeTb4b4b4, Te6edf3attemptsTb4b4b4, Te6edf3causeTb4b4b4) Tb4b4b4}
Tff7b72return Te6edf3connectedTransport Tff7b72?: Tff7b72throw Te6edf3postConfirmConnectionFailedTb4b4b4(Te6edf3rebootModeTb4b4b4, Te6edf3attemptsTb4b4b4, Tff7b72nullTb4b4b4)
Tb4b4b4}

Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffconnectTransportAttemptTb4b4b4(Te6edf3transportTb4b4b4: Te6edf3UnifiedOtaProtocolTb4b4b4, Te6edf3rebootModeTb4b4b4: Tffa657IntTb4b4b4)Tb4b4b4: Te6edf3ResultTff7b72<Tffa657UnitTff7b72> Tb4b4b4{
Tff7b72val Te6edf3connectResult Tff7b72=
Tff7b72try Tb4b4b4{
Te6edf3transportTb4b4b4.Te6edf3connectTb4b4b4(Tb4b4b4)
Tb4b4b4} Tff7b72catch Tb4b4b4(Te6edf3eTb4b4b4: Te6edf3CancellationExceptionTb4b4b4) Tb4b4b4{
Te6edf3closeFailedTransportTb4b4b4(Te6edf3transportTb4b4b4)
Tff7b72throw Te6edf3e
Tb4b4b4} Tff7b72catch Tb4b4b4(Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffTooGenericExceptionCaughtTa5d6ff"Tb4b4b4) Te6edf3eTb4b4b4: Te6edf3ExceptionTb4b4b4) Tb4b4b4{
Te6edf3ResultTb4b4b4.Te6edf3failureTb4b4b4(Te6edf3eTb4b4b4)
Tb4b4b4}

Tff7b72val Te6edf3error Tff7b72= Te6edf3connectResultTb4b4b4.Te6edf3exceptionOrNullTb4b4b4(Tb4b4b4) Tff7b72?: Tff7b72return Te6edf3connectResult
Tff7b72if Tb4b4b4(Te6edf3error Tff7b72is Te6edf3CancellationExceptionTb4b4b4) Tb4b4b4{
Te6edf3closeFailedTransportTb4b4b4(Te6edf3transportTb4b4b4)
Tff7b72throw Te6edf3error
Tb4b4b4}
Te6edf3LoggerTb4b4b4.Te6edf3wTb4b4b4(Te6edf3errorTb4b4b4) Tb4b4b4{
Ta5d6ff"Ta5d6ffESP32 OTA: Tffd700${Te6edf3otaModeNameTb4b4b4(Te6edf3rebootModeTb4b4b4)Tffd700}Ta5d6ff connection attempt failed; closing transport before retryTa5d6ff"
Tb4b4b4}
Te6edf3closeFailedTransportTb4b4b4(Te6edf3transportTb4b4b4)
Tff7b72return Te6edf3ResultTb4b4b4.Te6edf3failureTb4b4b4(Te6edf3errorTb4b4b4)
Tb4b4b4}

Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffcloseFailedTransportTb4b4b4(Te6edf3transportTb4b4b4: Te6edf3UnifiedOtaProtocolTb4b4b4) Tb4b4b4{
Te6edf3withContextTb4b4b4(Te6edf3NonCancellableTb4b4b4) Tb4b4b4{
Tff7b72try Tb4b4b4{
Te6edf3transportTb4b4b4.Te6edf3closeTb4b4b4(Tb4b4b4)
Tb4b4b4} Tff7b72catch Tb4b4b4(Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffTooGenericExceptionCaughtTa5d6ff"Tb4b4b4) Te6edf3eTb4b4b4: Te6edf3ThrowableTb4b4b4) Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3wTb4b4b4(Te6edf3eTb4b4b4) Tb4b4b4{ Ta5d6ff"Ta5d6ffESP32 OTA: Failed to close failed transport attemptTa5d6ff" Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}

Tff7b72private Tff7b72fun Td2a8ffpostConfirmConnectionFailedTb4b4b4(
Te6edf3rebootModeTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3attemptsTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3causeTb4b4b4: Te6edf3Throwable?Tb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3OtaProtocolExceptionTb4b4b4.Te6edf3ConnectionFailed Tb4b4b4{
Tff7b72val Te6edf3modeName Tff7b72= Te6edf3otaModeNameTb4b4b4(Te6edf3rebootModeTb4b4b4)
Tff7b72return Te6edf3OtaProtocolExceptionTb4b4b4.Te6edf3ConnectionFailedTb4b4b4(
Ta5d6ff"Ta5d6ffDevice confirmed Tffd700$Te6edf3modeNameTa5d6ff OTA mode, but the OTA service was not reachable after Tffd700$Te6edf3attemptsTa5d6ff attempts. Ta5d6ff" Tff7b72+
Te6edf3connectionRecoveryHintTb4b4b4(Te6edf3rebootModeTb4b4b4)Tb4b4b4,
Te6edf3causeTb4b4b4,
Tb4b4b4)
Tb4b4b4}

Tff7b72private Tff7b72fun Td2a8ffconnectionRecoveryHintTb4b4b4(Te6edf3rebootModeTb4b4b4: Tffa657IntTb4b4b4)Tb4b4b4: Tffa657String Tff7b72= Tff7b72when Tb4b4b4(Te6edf3rebootModeTb4b4b4) Tb4b4b4{
Te6edf3REBOOT_MODE_WIFI Tff7b72-Tff7b72> Ta5d6ff"Ta5d6ffMake sure the phone and device are on the same network and try again.Ta5d6ff"
Te6edf3REBOOT_MODE_BLE Tff7b72-Tff7b72> Ta5d6ff"Ta5d6ffKeep the device nearby and try again.Ta5d6ff"
Tff7b72else Tff7b72-Tff7b72> Ta5d6ff"Ta5d6ffTry again.Ta5d6ff"
Tb4b4b4}

Tf0883e@SuppressTb4b4b4(Ta5d6ff"Ta5d6ffLongMethodTa5d6ff"Tb4b4b4)
Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffexecuteOtaSequenceTb4b4b4(
Te6edf3transportTb4b4b4: Te6edf3UnifiedOtaProtocolTb4b4b4,
Te6edf3firmwareDataTb4b4b4: Te6edf3ByteArrayTb4b4b4,
Te6edf3sha256HashTb4b4b4: Tffa657StringTb4b4b4,
Te6edf3rebootModeTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3updateStateTb4b4b4: Tb4b4b4(Te6edf3FirmwareUpdateStateTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tb4b4b4) Tb4b4b4{
Tff7b72val Te6edf3fileSize Tff7b72= Te6edf3firmwareDataTb4b4b4.Te6edf3sizeTb4b4b4.Te6edf3toLongTb4b4b4(Tb4b4b4)
T8b949e// Start OTA handshake
Te6edf3updateStateTb4b4b4(
Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3ProcessingTb4b4b4(Te6edf3ProgressStateTb4b4b4(Te6edf3UiTextTb4b4b4.Te6edf3ResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_update_starting_otaTb4b4b4)Tb4b4b4)Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3transport
Tb4b4b4.Te6edf3startOtaTb4b4b4(Te6edf3sizeBytes Tff7b72= Te6edf3fileSizeTb4b4b4, Te6edf3sha256Hash Tff7b72= Te6edf3sha256HashTb4b4b4) Tb4b4b4{ Te6edf3status Tff7b72-Tff7b72>
Tff7b72when Tb4b4b4(Te6edf3statusTb4b4b4) Tb4b4b4{
Te6edf3OtaHandshakeStatusTb4b4b4.Te6edf3Erasing Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3updateStateTb4b4b4(
Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3ProcessingTb4b4b4(
Te6edf3ProgressStateTb4b4b4(Te6edf3UiTextTb4b4b4.Te6edf3ResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_update_erasingTb4b4b4)Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Tb4b4b4}
Tb4b4b4.Te6edf3getOrThrowTb4b4b4(Tb4b4b4)

T8b949e// Stream firmware data
Tff7b72val Te6edf3uploadingMsg Tff7b72= Te6edf3UiTextTb4b4b4.Te6edf3ResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_update_uploadingTb4b4b4)
Te6edf3updateStateTb4b4b4(Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3UpdatingTb4b4b4(Te6edf3ProgressStateTb4b4b4(Te6edf3uploadingMsgTb4b4b4, T79c0ff0fTb4b4b4)Tb4b4b4)Tb4b4b4)
Tff7b72val Te6edf3chunkSize Tff7b72=
Tff7b72if Tb4b4b4(Te6edf3rebootMode Tff7b72=Tff7b72= Te6edf3REBOOT_MODE_BLETb4b4b4) Tb4b4b4{
Te6edf3BleOtaTransportTb4b4b4.Te6edf3RECOMMENDED_CHUNK_SIZE
Tb4b4b4} Tff7b72else Tb4b4b4{
Te6edf3WifiOtaTransportTb4b4b4.Te6edf3RECOMMENDED_CHUNK_SIZE
Tb4b4b4}

Tff7b72val Te6edf3throughputTracker Tff7b72= Te6edf3ThroughputTrackerTb4b4b4(Tb4b4b4)
Te6edf3transport
Tb4b4b4.Te6edf3streamFirmwareTb4b4b4(
Te6edf3data Tff7b72= Te6edf3firmwareDataTb4b4b4,
Te6edf3chunkSize Tff7b72= Te6edf3chunkSizeTb4b4b4,
Te6edf3onProgress Tff7b72= Tb4b4b4{ Te6edf3progress Tff7b72-Tff7b72>
Tff7b72val Te6edf3bytesSent Tff7b72= Tb4b4b4(Te6edf3progress Tff7b72* Te6edf3firmwareDataTb4b4b4.Te6edf3sizeTb4b4b4)Tb4b4b4.Te6edf3toLongTb4b4b4(Tb4b4b4)
Te6edf3throughputTrackerTb4b4b4.Te6edf3recordTb4b4b4(Te6edf3bytesSentTb4b4b4)
Tff7b72val Te6edf3details Tff7b72=
Te6edf3formatTransferProgressTb4b4b4(Te6edf3progressTb4b4b4, Te6edf3firmwareDataTb4b4b4.Te6edf3sizeTb4b4b4, Te6edf3throughputTrackerTb4b4b4.Te6edf3bytesPerSecondTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3updateStateTb4b4b4(
Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3UpdatingTb4b4b4(
Te6edf3ProgressStateTb4b4b4(Te6edf3message Tff7b72= Te6edf3uploadingMsgTb4b4b4, Te6edf3progress Tff7b72= Te6edf3progressTb4b4b4, Te6edf3details Tff7b72= Te6edf3detailsTb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tb4b4b4.Te6edf3getOrThrowTb4b4b4(Tb4b4b4)
Te6edf3LoggerTb4b4b4.Te6edf3i Tb4b4b4{ Ta5d6ff"Ta5d6ffESP32 OTA: Firmware stream completedTa5d6ff" Tb4b4b4}

Te6edf3updateStateTb4b4b4(Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3SuccessTb4b4b4(Tb4b4b4)Tb4b4b4)
Tb4b4b4}

T8b949e/**
* Outcome of waiting for the device to confirm OTA entry after [triggerRebootOta]. The firmware emits a single
* [org.meshtastic.proto.ClientNotification] before its scheduled reboot covering all cases: success ("Rebooting to
* <BLE|WiFi> OTA") or rejection (any other OTA status message β€” partition missing, loader incompatible, switch
* failed). Older firmware may be silent, which falls back to the previous disconnect/reconnect path.
*/
Tff7b72private Tff7b72sealed Tff7b72interface T56d364OtaPreflightResult Tb4b4b4{
Tff7b72val Te6edf3continueToOtaTransportTb4b4b4: Tffa657Boolean

T8b949e/** Firmware confirmed it is rebooting into the OTA loader. */
Tff7b72data Tff7b72object T56d364Confirmed Tb4b4b4: Te6edf3OtaPreflightResult Tb4b4b4{
Tff7b72override Tff7b72val Te6edf3continueToOtaTransport Tff7b72= Tff7b72true
Tb4b4b4}

T8b949e/** Firmware rejected the OTA request; [message] is the verbatim rejection reason. */
Tff7b72data Tff7b72class T56d364RejectedTb4b4b4(Tff7b72val Te6edf3messageTb4b4b4: Tffa657StringTb4b4b4) Tb4b4b4: Te6edf3OtaPreflightResult Tb4b4b4{
Tff7b72override Tff7b72val Te6edf3continueToOtaTransport Tff7b72= Tff7b72false
Tb4b4b4}

T8b949e/** No ClientNotification arrived within the bound; continue with the legacy fixed-delay path. */
Tff7b72data Tff7b72object T56d364LegacyFallback Tb4b4b4: Te6edf3OtaPreflightResult Tb4b4b4{
Tff7b72override Tff7b72val Te6edf3continueToOtaTransport Tff7b72= Tff7b72true
Tb4b4b4}
Tb4b4b4}

T8b949e/**
* Run the OTA preflight gate. On [OtaPreflightResult.Confirmed] this releases the mesh transport (with the GATT
* release delay) and returns Confirmed. On rejection it emits [FirmwareUpdateState.Error] and returns the failure
* result so the caller can abort while preserving the cleanup artifact and mesh transport. On legacy fallback it
* releases the mesh transport and continues to the OTA connection attempts just as older app versions did.
*/
Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffrunOtaPreflightTb4b4b4(
Te6edf3rebootModeTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3baselineMessageTb4b4b4: Tffa657String?Tb4b4b4,
Te6edf3updateStateTb4b4b4: Tb4b4b4(Te6edf3FirmwareUpdateStateTb4b4b4) Tff7b72-Tff7b72> Tffa657UnitTb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3OtaPreflightResult Tff7b72=
Tff7b72when Tb4b4b4(Tff7b72val Te6edf3preflight Tff7b72= Te6edf3awaitOtaConfirmationTb4b4b4(Te6edf3environmentTb4b4b4.Te6edf3otaPreflightTimeoutMsTb4b4b4, Te6edf3rebootModeTb4b4b4, Te6edf3baselineMessageTb4b4b4)Tb4b4b4) Tb4b4b4{
Tff7b72is Te6edf3OtaPreflightResultTb4b4b4.Te6edf3Confirmed Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3i Tb4b4b4{ Ta5d6ff"Ta5d6ffESP32 OTA: Preflight confirmed; releasing mesh transport for OTATa5d6ff" Tb4b4b4}
Te6edf3releaseMeshTransportForOtaTb4b4b4(Tb4b4b4)
Te6edf3preflight
Tb4b4b4}

Tff7b72is Te6edf3OtaPreflightResultTb4b4b4.Te6edf3Rejected Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3w Tb4b4b4{ Ta5d6ff"Ta5d6ffESP32 OTA: Firmware rejected OTA entry (Tffd700${Te6edf3preflightTb4b4b4.Te6edf3messageTffd700}Ta5d6ff); mesh transport preservedTa5d6ff" Tb4b4b4}
Te6edf3updateStateTb4b4b4(
Te6edf3FirmwareUpdateStateTb4b4b4.Te6edf3ErrorTb4b4b4(
Te6edf3UiTextTb4b4b4.Te6edf3ResourceTb4b4b4(Te6edf3ResTb4b4b4.Te6edf3stringTb4b4b4.Te6edf3firmware_update_ota_unsupported_reasonTb4b4b4, Te6edf3preflightTb4b4b4.Te6edf3messageTb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3preflight
Tb4b4b4}

Tff7b72is Te6edf3OtaPreflightResultTb4b4b4.Te6edf3LegacyFallback Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3w Tb4b4b4{
Ta5d6ff"Ta5d6ffESP32 OTA: No firmware confirmation within Tffd700${Te6edf3environmentTb4b4b4.Te6edf3otaPreflightTimeoutMsTffd700}Ta5d6ffms; Ta5d6ff" Tff7b72+
Ta5d6ff"Ta5d6ffusing legacy OTA reconnectTa5d6ff"
Tb4b4b4}
Te6edf3releaseMeshTransportForOtaTb4b4b4(Tb4b4b4)
Te6edf3preflight
Tb4b4b4}
Tb4b4b4}

Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffreleaseMeshTransportForOtaTb4b4b4(Tb4b4b4) Tb4b4b4{
Te6edf3disconnectMeshServiceTb4b4b4(Tb4b4b4)
T8b949e// Give BLE stack time to fully release the GATT connection.
Te6edf3environmentTb4b4b4.Te6edf3delayTb4b4b4(Te6edf3environmentTb4b4b4.Te6edf3gattReleaseDelayMsTb4b4b4)
Tb4b4b4}

T8b949e/**
* Race the firmware's OTA-entry ClientNotification against [timeoutMs]. The [baselineMessage] is captured by the
* caller BEFORE the OTA trigger is sent, so a stale notification already held in the StateFlow doesn't get mistaken
* for the response.
*/
Tff7b72private Tff7b72suspend Tff7b72fun Td2a8ffawaitOtaConfirmationTb4b4b4(
Te6edf3timeoutMsTb4b4b4: Tffa657LongTb4b4b4,
Te6edf3rebootModeTb4b4b4: Tffa657IntTb4b4b4,
Te6edf3baselineMessageTb4b4b4: Tffa657String?Tb4b4b4,
Tb4b4b4)Tb4b4b4: Te6edf3OtaPreflightResult Tb4b4b4{
Tff7b72val Te6edf3modeName Tff7b72= Te6edf3otaModeNameTb4b4b4(Te6edf3rebootModeTb4b4b4)
Tff7b72val Te6edf3matched Tff7b72=
Te6edf3withTimeoutOrNullTb4b4b4(Te6edf3timeoutMsTb4b4b4) Tb4b4b4{
Te6edf3radioControllerTb4b4b4.Te6edf3clientNotificationTb4b4b4.Te6edf3first Tb4b4b4{ Te6edf3cn Tff7b72-Tff7b72>
Te6edf3cn Tff7b72!Tff7b72= Tff7b72null Tff7b72&Tff7b72& Te6edf3cnTb4b4b4.Te6edf3message Tff7b72!Tff7b72= Te6edf3baselineMessage Tff7b72&Tff7b72& Te6edf3cnTb4b4b4.Te6edf3isOtaStatusNotificationTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}
Te6edf3matched Tff7b72?: Tff7b72return Te6edf3OtaPreflightResultTb4b4b4.Te6edf3LegacyFallback

Te6edf3radioControllerTb4b4b4.Te6edf3clearClientNotificationTb4b4b4(Tb4b4b4)

Tff7b72return Tff7b72when Tb4b4b4{
T8b949e// Defense-in-depth: require both the canonical "Rebooting to" prefix AND the requested mode name in the
T8b949e// message. Firmware is consistent today, but this guards against a future firmware bug that acks the wrong
T8b949e// mode β€” a wrong-mode confirmation would route the host to a transport the device did not enter.
Te6edf3matchedTb4b4b4.Te6edf3messageTb4b4b4.Te6edf3startsWithTb4b4b4(Te6edf3OTA_CONFIRM_PREFIXTb4b4b4, Te6edf3ignoreCase Tff7b72= Tff7b72trueTb4b4b4) Tff7b72&Tff7b72&
Te6edf3matchedTb4b4b4.Te6edf3messageTb4b4b4.Te6edf3containsTb4b4b4(Te6edf3modeNameTb4b4b4, Te6edf3ignoreCase Tff7b72= Tff7b72trueTb4b4b4) Tff7b72-Tff7b72> Tb4b4b4{
Te6edf3LoggerTb4b4b4.Te6edf3i Tb4b4b4{ Ta5d6ff"Ta5d6ffESP32 OTA: Firmware confirmed Tffd700$Te6edf3modeNameTa5d6ff OTA entry: Tffd700${Te6edf3matchedTb4b4b4.Te6edf3messageTffd700}Ta5d6ff" Tb4b4b4}
Te6edf3OtaPreflightResultTb4b4b4.Te6edf3Confirmed
Tb4b4b4}

Tff7b72else Tff7b72-Tff7b72> Te6edf3OtaPreflightResultTb4b4b4.Te6edf3RejectedTb4b4b4(Te6edf3matchedTb4b4b4.Te6edf3messageTb4b4b4)
Tb4b4b4}
Tb4b4b4}

Tff7b72private Tff7b72fun Td2a8ffotaModeNameTb4b4b4(Te6edf3rebootModeTb4b4b4: Tffa657IntTb4b4b4)Tb4b4b4: Tffa657String Tff7b72= Tff7b72when Tb4b4b4(Te6edf3rebootModeTb4b4b4) Tb4b4b4{
Te6edf3REBOOT_MODE_BLE Tff7b72-Tff7b72> Ta5d6ff"Ta5d6ffBLETa5d6ff"
Te6edf3REBOOT_MODE_WIFI Tff7b72-Tff7b72> Ta5d6ff"Ta5d6ffWiFiTa5d6ff"
Tff7b72else Tff7b72-Tff7b72> Te6edf3rebootModeTb4b4b4.Te6edf3toStringTb4b4b4(Tb4b4b4)
Tb4b4b4}
Tb4b4b4}

Served by rngit 1.5.0 - Generated in 0.15s